package wiz.client.activities.playhand;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import wiz.client.ClientFactory;
import wiz.client.WizStrings;
import wiz.client.activities.playgame.GameInProgressPlace;
import wiz.client.activities.playhand.PlayHandView.WizPlayerHandState;
import wiz.client.events.BidDecreasedEvent;
import wiz.client.events.BidIncreasedEvent;
import wiz.client.events.RoundFinishedEvent;
import wiz.client.events.RoundStartedEvent;
import wiz.client.events.TricksMadeDecreasedEvent;
import wiz.client.events.TricksMadeIncreasedEvent;
import wiz.client.model.WizGameModel;
import wiz.client.model.WizGameRound;
import wiz.client.model.WizPlayer;
import wiz.client.model.WizPlayerRoundScore;

import com.google.gwt.activity.shared.AbstractActivity;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AcceptsOneWidget;

public class PlayHandActivity extends AbstractActivity {

	public static class GameStateCache {
		private String fGameStateToken;
		private WizGameModel fGameState;
		
		
		
		public GameStateCache(String gameStateToken, WizGameModel gameState) {
			super();
			fGameStateToken = gameStateToken;
			fGameState = gameState;
		}

		public void setState(String token, WizGameModel state){
			fGameStateToken = token;
			fGameState = state;
		}
		
		public String getGameStateToken(){
			return fGameStateToken;
		}
		
		public WizGameModel getGameState() {
			return fGameState;
		}
	}

	private ClientFactory fClientFactory;
	private PlayHandView fView;
	
	
	
	private List<WizPlayerHandState> fPlayerHands = new ArrayList<WizPlayerHandState>();
	private GameStateCache fGameStateCache;
	private final WizStrings fWizStrings = GWT.create(WizStrings.class);


	public PlayHandActivity(ClientFactory clientFactory) {
		fClientFactory = clientFactory;
		PlayHandView view = fClientFactory.buildPlayHandView();
		fView = view;
		initHandlers();
	}

	@Override
	public void start(AcceptsOneWidget panel, EventBus eventBus) {
		panel.setWidget(fView);

		int suggestNewHandSize = fClientFactory.getWizGameDetails().suggestNewHandSize(fGameStateCache.fGameState);
		fView.getRoundSizeSelector().getValueText().setText(suggestNewHandSize + ""); //$NON-NLS-1$
		
		refreshView();
		Window.scrollTo(0, 0);
	}

	private void refreshView() {
		//apply any validation
		//update bids/made values
		
		
		
		
		WizGameModel loadGameState = fGameStateCache.fGameState;
		WizGameRound wizGameRound = loadGameState.getGameRounds().get(loadGameState.getGameRounds().size() - 1);
		
		fView.setBidValidationVisible(!wizGameRound.hasRoundStarted());
		fView.setScoreValidationVisible(wizGameRound.hasRoundStarted());
		
		List<WizPlayerRoundScore> roundScores = wizGameRound.getRoundScores();
		int totalBid = 0;
		for (WizPlayerRoundScore wizPlayerRoundScore : roundScores) {
			totalBid += wizPlayerRoundScore.getTricksBid();
		}
		
		fView.getTotalBidText().setText(fWizStrings.totalBid(totalBid));
		
		if(wizGameRound.hasRoundStarted()){
			
			fView.getStartRoundButtonVisibility().setEnabled(false);
			
			String tricksMadeValidation = fClientFactory.getWizGameDetails().isValidTricksMade(wizGameRound);
			if(tricksMadeValidation != null){
				fView.getRoundValidationText().setText(tricksMadeValidation);
				fView.getDoneRoundButtonEnabled().setEnabled(false);
			} else {
				fView.getRoundValidationText().setText(""); //$NON-NLS-1$
				fView.getDoneRoundButtonEnabled().setEnabled(true);
			}
		} else {
			
			String validBid = fClientFactory.getWizGameDetails().isValidBid(wizGameRound);
			if(validBid != null){
				fView.getBidValidationText().setText(validBid);	
			} else {
				fView.getBidValidationText().setText(""); //$NON-NLS-1$
			}
			
			fView.getStartRoundButtonVisibility().setEnabled(
					fClientFactory.getWizGameDetails().isValidHandSize(wizGameRound.getRoundHandSize()) && 
					validBid == null);
			

			
			fView.getRoundValidationText().setText(""); //$NON-NLS-1$
			fView.getDoneRoundButtonEnabled().setEnabled(false);
		}
		
//		fView.getRoundSizeSelector().getDecreaseButtonEnabled().setEnabled(!wizGameRound.hasRoundStarted());
		fView.getRoundSizeSelector().getIncreaseButtonEnabled().setEnabled(!wizGameRound.hasRoundStarted());
		fView.getRoundSizeSelector().getValueText().setText("" + wizGameRound.getRoundHandSize()); //$NON-NLS-1$
		
		
		
		
		for (WizPlayerHandState handView : fPlayerHands) {
//			handView.getBidSelector().getDecreaseButtonEnabled().setEnabled(!wizGameRound.hasRoundStarted());
			handView.getBidSelector().getIncreaseButtonEnabled().setEnabled(!wizGameRound.hasRoundStarted());
//			handView.getMadeSelector().getDecreaseButtonEnabled().setEnabled(wizGameRound.hasRoundStarted());
			handView.getMadeSelector().getIncreaseButtonEnabled().setEnabled(wizGameRound.hasRoundStarted());
		}
		
		for (WizPlayerRoundScore playerRoundScore : wizGameRound.getRoundScores()) {
			WizPlayerHandState loadStateForPlayer = loadStateForPlayer(playerRoundScore.getPlayer());
			loadStateForPlayer.getBidSelector().getValueText().setText(playerRoundScore.getTricksBid() + ""); //$NON-NLS-1$
			loadStateForPlayer.getMadeSelector().getValueText().setText(playerRoundScore.getTricksMade() + ""); //$NON-NLS-1$
			
			//set made-bid indicator
			loadStateForPlayer.getMadeBid().setValue(wizGameRound.hasRoundStarted() && playerRoundScore.getTricksBid() == playerRoundScore.getTricksMade());
		}
		
		
		//calculate total scores
		Map<String, Integer> scores = new HashMap<String, Integer>();
		
		//loadGameState.getGameRounds() contains the current round. Don't include in the score calculation!
		for(int i = 0; i < loadGameState.getGameRounds().size() - 1; i++){
			WizGameRound round = loadGameState.getGameRounds().get(i);
			for (WizPlayerRoundScore roundScore : round.getRoundScores()) {
				Integer current = scores.get(roundScore.getPlayer().getName());
				if(current == null){
					current = 0;
				}
				
				scores.put(roundScore.getPlayer().getName(), fClientFactory.getWizGameDetails().calculateNewScore(current, roundScore));		
			}
		}
		
		for (WizPlayerHandState hand : fPlayerHands) {
			Integer score = scores.get(hand.getPlayer().getText());
			if(score == null){
				score = 0;
			}
			hand.getPlayerScore().setText("" + score);
		}

		
	}

	private void initHandlers() {
		fView.getDoneRoundButtonClickHandlers().addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				doneHandClicked();
			}
		});
		
//		fView.getRoundSizeSelector().getDecreaseButtonClickHandlers().addClickHandler(new ClickHandler() {
//			
//			@Override
//			public void onClick(ClickEvent event) {
//				roundSizeDecreased();
//				
//			}
//		});
		
		fView.getRoundSizeSelector().getIncreaseButtonClickHanlders().addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				roundSizeIncreased();
				
			}
		});
		
		fView.getStartRoundButtonClickHandlers().addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				startRoundClicked();
			}
		});
		
	}

	protected void startRoundClicked() {
		WizGameModel loadGameState = fGameStateCache.fGameState;
		WizGameRound wizGameRound = loadGameState.getGameRounds().get(loadGameState.getGameRounds().size() - 1);
		wizGameRound.setRoundStarted(true);
		
		String newGameStateToken = fClientFactory.getGameStateStorage().storeGameState(loadGameState);
		fGameStateCache = new GameStateCache(newGameStateToken, loadGameState);
		
		fClientFactory.getPlaceController().goTo(new PlayHandPlace(newGameStateToken));
		
		fClientFactory.getEventBus().fireEvent(new RoundStartedEvent());
		
	}

	protected void doneHandClicked() {
		fClientFactory.getPlaceController().goTo(new GameInProgressPlace(fGameStateCache.getGameStateToken()));
		fClientFactory.getEventBus().fireEvent(new RoundFinishedEvent());

	}

	public void setGameStateToken(String gameStateToken) {
		
		if(fGameStateCache == null ||  !gameStateToken.equals(fGameStateCache.getGameStateToken())){
			WizGameModel loadGameState = loadGameState(gameStateToken);
			fGameStateCache = new GameStateCache(gameStateToken, loadGameState);
		}
		

		List<WizPlayer> wizPlayers = fGameStateCache.getGameState().getWizPlayers();
		for (WizPlayer wizPlayer : wizPlayers) {
			if(!wizPlayerUIExists(wizPlayer)){
				addSelectorForWizPlayer(wizPlayer);
			}
		}
		
		//remove selectors for players that don't exist
		for(int i = fPlayerHands.size() - 1; i >= 0; i--){
			WizPlayerHandState wizPlayerHandState = fPlayerHands.get(i);
			boolean playerExists = false;
			for (WizPlayer wizPlayer : wizPlayers) {
				if(wizPlayer.getName().equals(wizPlayerHandState.getPlayer().getText())){
					playerExists = true;
					break;
				}
			}
			if(!playerExists){
				fView.removePlayerUI(i);
				fPlayerHands.remove(i);
			}
		}
		
		
		
		refreshView();
	}

	private WizGameModel loadGameState(String gameStateToken) {
		WizGameModel loadGameState = fClientFactory.getGameStateStorage().loadGameState(gameStateToken);
		
		//we are always in the latest round. precondition is that round must be created before we get here.
		if(loadGameState.getGameRounds().size() <= 0){
			throw new IllegalStateException();
		}
		return loadGameState;
	}


	private void addSelectorForWizPlayer(final WizPlayer wizPlayer) {
		WizPlayerHandState newPlayerState = fView.createAndAddPlayerHandState();
		newPlayerState.getPlayer().setText(wizPlayer.getName());
//		newPlayerState.getBidSelector().getDecreaseButtonClickHandlers().addClickHandler(new ClickHandler(){
//
//			@Override
//			public void onClick(ClickEvent event) {
//				decreasePlayerBid(wizPlayer);
//			}
//		});
		
		newPlayerState.getBidSelector().getIncreaseButtonClickHanlders().addClickHandler(new ClickHandler(){

			@Override
			public void onClick(ClickEvent event) {
				increasePlayerBid(wizPlayer);
			}
		});
		
		
//		newPlayerState.getMadeSelector().getDecreaseButtonClickHandlers().addClickHandler(new ClickHandler(){
//
//			@Override
//			public void onClick(ClickEvent event) {
//				decreasePlayerTricksMade(wizPlayer);
//			}
//		});
		
		newPlayerState.getMadeSelector().getIncreaseButtonClickHanlders().addClickHandler(new ClickHandler(){

			@Override
			public void onClick(ClickEvent event) {
				increasePlayerTricksMade(wizPlayer);
			}
		});
		
		fPlayerHands.add(newPlayerState);
	}

	private WizPlayerHandState loadStateForPlayer(WizPlayer player) {
		for (WizPlayerHandState hand : fPlayerHands) {
			if(hand.getPlayer().getText().equals(player.getName())){
				return hand;
			}
		}
		return null;
	}

	protected void roundSizeDecreased() {
		adjustRoundSize(-1);
	}
	

	protected void roundSizeIncreased() {
		adjustRoundSize(1);
	}

	
	private void adjustRoundSize(int i) {
		WizGameModel loadGameState = fGameStateCache.getGameState();
		WizGameRound wizGameRound = loadGameState.getGameRounds().get(loadGameState.getGameRounds().size() - 1);
		wizGameRound.setRoundHandSize(wizGameRound.getRoundHandSize() + i);
		
		
		
		String newGameStateToken = fClientFactory.getGameStateStorage().storeGameState(loadGameState);
		fGameStateCache = new GameStateCache(newGameStateToken, loadGameState);

		fClientFactory.getPlaceController().goTo(new PlayHandPlace(newGameStateToken));
	}

	protected void increasePlayerTricksMade(WizPlayer wizPlayer) {
		adjustTricksMade(wizPlayer, 1);
		fClientFactory.getEventBus().fireEvent(new TricksMadeIncreasedEvent());

	}

	protected void decreasePlayerTricksMade(WizPlayer wizPlayer) {
		adjustTricksMade(wizPlayer, -1);
		fClientFactory.getEventBus().fireEvent(new TricksMadeDecreasedEvent());

	}

	private void adjustTricksMade(WizPlayer wizPlayer, int i) {
		WizGameModel loadGameState = fGameStateCache.getGameState();
		WizGameRound wizGameRound = loadGameState.getGameRounds().get(loadGameState.getGameRounds().size() - 1);
		for (WizPlayerRoundScore scores : wizGameRound.getRoundScores()) {
			if(scores.getPlayer().getName().equals(wizPlayer.getName())){
				scores.setTricksMade(scores.getTricksMade()+ i);
				
				String newGameStateToken = fClientFactory.getGameStateStorage().storeGameState(loadGameState);
				fGameStateCache = new GameStateCache(newGameStateToken, loadGameState);
				
				fClientFactory.getPlaceController().goTo(new PlayHandPlace(newGameStateToken));
				break;
			}
		}
	}
	
	
	
	
	protected void increasePlayerBid(WizPlayer wizPlayer) {
		fClientFactory.getEventBus().fireEvent(new BidIncreasedEvent());
		adjustPlayerBid(wizPlayer, 1);
	}

	private void adjustPlayerBid(WizPlayer wizPlayer, int i) {
		WizGameModel loadGameState = fGameStateCache.getGameState();

		WizGameRound wizGameRound = loadGameState.getGameRounds().get(loadGameState.getGameRounds().size() - 1);
		for (WizPlayerRoundScore scores : wizGameRound.getRoundScores()) {
			if(scores.getPlayer().getName().equals(wizPlayer.getName())){
				scores.setTricksBid(scores.getTricksBid()+i);
				
				String newGameStateToken = fClientFactory.getGameStateStorage().storeGameState(loadGameState);
				fGameStateCache = new GameStateCache(newGameStateToken, loadGameState);
				
				fClientFactory.getPlaceController().goTo(new PlayHandPlace(newGameStateToken));
				break;
			}
		}		
	}

	protected void decreasePlayerBid(WizPlayer wizPlayer) {
		fClientFactory.getEventBus().fireEvent(new BidDecreasedEvent());
		adjustPlayerBid(wizPlayer, -1);
		
	}

	private boolean wizPlayerUIExists(WizPlayer wizPlayer) {
		for (WizPlayerHandState playerHand : fPlayerHands) {
			if(playerHand.getPlayer().getText().equals(wizPlayer.getName())){
				return true;
			}
			
		}
		return false;
	}

}
	